#pragma once
#include <vector>

enum { NumBitsPerDWORD = 32 };
enum { NumBitsPerDWORDLogTwo = 5 };

// BitReference
class BitReference
{
public:
	BitReference(size_t& data, size_t mask) : mData(data), mMask(mask) {}

	operator bool() const { return (mData & mMask) != 0; }
	void operator=(const bool v)
	{
		if (v) mData |= mMask;
		else mData &= ~mMask;
	}

private:
	size_t& mData;
	size_t mMask;
};

// ConstBitReference
class ConstBitReference
{
public:
	ConstBitReference(const size_t& data, size_t mask) : mData(data), mMask(mask) {}
	operator bool() const { return (mData & mMask) != 0; }

private:
	const size_t& mData;
	size_t mMask;
};

// RelativeBitReference
class RelativeBitReference
{
public:
	RelativeBitReference(size_t bitIndex)
		: mDWORDIndex(bitIndex >> NumBitsPerDWORDLogTwo)
		, mMask(1 << (bitIndex & (NumBitsPerDWORD - 1)))
	{}

protected:
	size_t mDWORDIndex;
	size_t mMask;
};

// BitSet
class BitSet
{
public:
	BitSet(bool value = false, size_t size = 0) 
	{
		Init(value, size);
	}

	void Init(bool value, size_t size)
	{
		mSize = size;
		if (mSize == 0)
			mBits.clear();
		else
		{
			mBits.resize((mSize + NumBitsPerDWORD - 1) / NumBitsPerDWORD);
			memset(&mBits[0], value ? 0xff : 0x00, mBits.size() * sizeof(size_t));
		}
	}

	// clear this bitset.
	void Empty()
	{
		mSize = 0;
		mBits.clear();
	}

	// add a bool value to this bitset.
	void Add(bool value)
	{
		mSize++;

		size_t off = mSize % NumBitsPerDWORD;
		size_t num = (mSize + NumBitsPerDWORD - 1) / NumBitsPerDWORD;
		if (num == mBits.size())
			mBits.push_back(value ? 0x01 : 0x00);
		else
		{
			(*this)(mSize - 1) = value;
		}
	}

	inline size_t Size() const { return mSize; }
	inline BitReference operator()(size_t index) 
	{
		return BitReference(
			mBits[index / NumBitsPerDWORD], 
			1 << (index & (NumBitsPerDWORD - 1)));
	}
	inline const ConstBitReference operator()(size_t index) const
	{
		return ConstBitReference(
			mBits[index / NumBitsPerDWORD], 
			1 << (index & (NumBitsPerDWORD - 1)));
	}

private:
	size_t mSize;
	std::vector<size_t> mBits;
};